package cn.lingyangwl.agile.auth.third;

import cn.lingyangwl.agile.auth.constants.CacheKeyEnum;
import cn.lingyangwl.agile.auth.model.rqrs.third.AuthUrlGetReq;
import cn.lingyangwl.agile.auth.model.rqrs.third.ThirdAuthResp;
import cn.lingyangwl.agile.auth.model.rqrs.third.ThirdAuthUser;
import cn.lingyangwl.agile.auth.model.rqrs.third.ThirdLoginReq;
import cn.lingyangwl.agile.model.enums.ConfigDefineEnum;
import cn.lingyangwl.agile.model.module.auth.GrantTypeEnum;
import cn.lingyangwl.agile.model.module.config.ConfigInfo;
import cn.lingyangwl.agile.model.module.config.param.BaseConfigParam;
import cn.lingyangwl.agile.model.module.config.param.DingTalkParam;
import cn.lingyangwl.framework.tool.core.exception.Assert;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.framework.tool.core.http.UrlEncoder;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.github.yitter.idgen.YitIdHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 钉钉授权登录
 *
 * @author shenguangyang
 */
@Slf4j
@Component
public class DingTalkAuth extends BaseThirdAuth {
    private static final String AUTH_URL = "https://login.dingtalk.com/oauth2/auth?redirect_uri=%s&response_type=code&client_id=%s&scope=openid&state=%s&prompt=consent";

    @Override
    public GrantTypeEnum grantType() {
        return GrantTypeEnum.DINGTALK;
    }


    public static Client authClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    @Override
    public ThirdAuthUser doLogin(ThirdLoginReq req) {
        Assert.notEmpty(req.getBackUrl(), "backUrl 不能为空");
        CacheKeyEnum keyEnum = CacheKeyEnum.OAUTH_STATE;
        String cacheKey = keyEnum.formatKey(req.getState());
        if (Boolean.FALSE.equals(redisTemplate.delete(cacheKey))) {
            throw new BizException("无效的state, 请重新发起登录");
        }

        ConfigInfo config = configFactory.getConfigOfNonNull(ConfigDefineEnum.DINGTALK);
        DingTalkParam param = BaseConfigParam.jsonToObject(config.getConfigKey(), config.getConfigValue());
        String returnUrl = param.getAuthCallbackUrl();

        ThirdAuthUser authUser = new ThirdAuthUser();
        try {
            com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
            // 获取用户信息
            GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                    //应用基础信息-应用信息的AppKey,请务必替换为开发的应用AppKey
                    .setClientId(param.getAppKey())
                    //应用基础信息-应用信息的AppSecret，,请务必替换为开发的应用AppSecret
                    .setClientSecret(param.getAppSecret())
                    .setCode(req.getAuthCode())
                    .setGrantType("authorization_code");
            GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
            //获取用户个人token
            String accessToken = getUserTokenResponse.getBody().getAccessToken();
            JSONObject userInfo = getUserinfo(accessToken);

            authUser.setRawUserInfo(userInfo);
            authUser.setNickname(userInfo.getString("nick"));
            authUser.setAvatar(userInfo.getString("avatarUrl"));
            // 如果要获取用户手机号，需要在开发者后台申请个人手机号信息权限
            authUser.setPhone(userInfo.getString("mobile"));
            authUser.setOpenId(userInfo.getString("openId"));
            authUser.setEmail(userInfo.getString("email"));
        } catch (Exception e) {
            log.error("error: {}", e.getMessage());
            throw new BizException("授权失败, 请尝试重新发起登录");
        }

        authUser.setRedirectUrl(returnUrl);
        // 优先以前端为准
        authUser.setIsRedirect(false);
        return authUser;
    }

    public static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkcontact_1_0.Client(config);
    }

    /**
     * 获取用户个人信息
     */
    public JSONObject getUserinfo(String accessToken) throws Exception {
        com.aliyun.dingtalkcontact_1_0.Client client = contactClient();
        GetUserHeaders getUserHeaders = new GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
        //获取用户个人信息，如需获取当前授权人的信息，unionId参数必须传me
        String me = JSON.toJSONString(client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody());
        System.out.println(me);
        return JSON.parseObject(me);
    }

    @Override
    public ThirdAuthResp doAuthorize(AuthUrlGetReq req) {
        ConfigInfo config = configFactory.getConfigOfNonNull(ConfigDefineEnum.DINGTALK);
        DingTalkParam param = BaseConfigParam.jsonToObject(config.getConfigKey(), config.getConfigValue());
        Assert.notEmpty(req.getBackUrl(), "backUrl 不能为空");

        // 拼接回调uri
        String redirectUri = super.getThirdRedirectUri(param.getAuthCallbackUrl(), req.getBackUrl(), req.getClient());

        String state = String.valueOf(YitIdHelper.nextId());
        String pageUrl = String.format(AUTH_URL, UrlEncoder.urlEncode(redirectUri), param.getAppKey(), state);

        CacheKeyEnum keyEnum = CacheKeyEnum.OAUTH_STATE;
        String cacheKey = keyEnum.formatKey(state);
        redisTemplate.opsForValue().set(cacheKey, "", keyEnum.getExpire(), keyEnum.getUnit());
        return ThirdAuthResp.builder().pageUrl(pageUrl).state(state).build();
    }
}
